/***************************************************
*		 Copyright (c) 2018 MINE 田宇
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
***************************************************/

#include "lib.h"
#include "printk.h"
#include "PCI.h"

unsigned int Read_PCI_Config(unsigned int bus,unsigned int device,unsigned int function,unsigned int offset)
{
	unsigned int address = 0x80000000 | ((bus & 0xff) << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8) | (offset & 0xfc);
	io_mfence();
	io_out32(0xcf8,address);
	io_mfence();
	return io_in32(0xcfc);
}

void Write_PCI_Config(unsigned int bus,unsigned int device,unsigned int function,unsigned int offset,unsigned int value)
{
	unsigned int address = 0x80000000 | ((bus & 0xff) << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8) | (offset & 0xfc);
	io_mfence();
	io_out32(0xcf8,address);
	io_mfence();
	io_out32(0xcfc,value);
}

int analysis_PCI_Config(struct PCI_Header_00 * PCI_HDR,unsigned int bus,unsigned int device,unsigned int function)
{
	unsigned int value = 0;
	unsigned int index = 0;

	memset(PCI_HDR,0,sizeof(struct PCI_Header_00));
	PCI_HDR->BDF = ((bus & 0xff) << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8); 

	value = Read_PCI_Config(bus,device,function,0);
	PCI_HDR->VendorID = value & 0xffff;
	PCI_HDR->DeviceID = (value >> 16) & 0xffff;
	color_printk(RED,BLACK,"PCI Bus:%#04x,Dev:%#04x,Func:%#03x,DID:%#06x,VID:%#06x\n",bus,device,function,PCI_HDR->DeviceID,PCI_HDR->VendorID);

	value = Read_PCI_Config(bus,device,function,4);
	PCI_HDR->Command = value & 0xffff;
	PCI_HDR->Status = (value >> 16) & 0xffff;
	color_printk(RED,BLACK,"Status:%#06x,Commond:%#06x,",PCI_HDR->Status,PCI_HDR->Command);

	value = Read_PCI_Config(bus,device,function,8);
	PCI_HDR->RevisionID = value & 0xff;
	PCI_HDR->ProgIF = (value >> 8) & 0xff;
	PCI_HDR->SubClass = (value >> 16) & 0xff;
	PCI_HDR->ClassCode = (value >> 24) & 0xff;
	color_printk(RED,BLACK,"Class Code:%#04x,SubClass:%#04x,Prog IF:%#04x,Revision ID:%#04x,",PCI_HDR->ClassCode,PCI_HDR->SubClass,PCI_HDR->ProgIF,PCI_HDR->RevisionID);

	value = Read_PCI_Config(bus,device,function,12);
	PCI_HDR->CacheLineSize = value & 0xff;
	PCI_HDR->LatencyTimer = (value >> 8) & 0xff;
	PCI_HDR->HeaderType = (value >> 16) & 0xff;
	PCI_HDR->BIST = (value >> 24) & 0xff;
	color_printk(RED,BLACK,"BIST:%#04x,Header Type:%#04x,Latency Timer:%#04x,Cache LineSize:%#04x\n",PCI_HDR->BIST,PCI_HDR->HeaderType,PCI_HDR->LatencyTimer,PCI_HDR->CacheLineSize);

	value = Read_PCI_Config(bus,device,function,16);	//////BAR0
	if(value & 1)
	{
		PCI_HDR->BaseAddress0 = value & 0xfffffffc;
	}
	else
		PCI_HDR->BaseAddress0 = value & 0xfffffff0;
	if(PCI_HDR->BaseAddress0)
	{
		Write_PCI_Config(bus,device,function,16,0xffffffff);
		value = Read_PCI_Config(bus,device,function,16);
		value = value & 0xfffffff0;
		PCI_HDR->BaseLimit0 = ~value + 1;
		Write_PCI_Config(bus,device,function,16,PCI_HDR->BaseAddress0);
	}
	color_printk(RED,BLACK,"Base Address #0:%#010x,Base Limit #0:%#010x\n",PCI_HDR->BaseAddress0,PCI_HDR->BaseLimit0);

	value = Read_PCI_Config(bus,device,function,20);	//////BAR1
	if(value & 1)
	{
		PCI_HDR->BaseAddress1 = value & 0xfffffffc;
	}
	else
		PCI_HDR->BaseAddress1 = value & 0xfffffff0;
	if(PCI_HDR->BaseAddress1)
	{
		Write_PCI_Config(bus,device,function,20,0xffffffff);
		value = Read_PCI_Config(bus,device,function,20);
		value = value & 0xfffffff0;;
		PCI_HDR->BaseLimit1 = ~value + 1;
		Write_PCI_Config(bus,device,function,20,PCI_HDR->BaseAddress1);
	}
	color_printk(RED,BLACK,"Base Address #1:%#010x,Base Limit #1:%#010x\n",PCI_HDR->BaseAddress1,PCI_HDR->BaseLimit1);

	value = Read_PCI_Config(bus,device,function,24);	//////BAR2
	if(value & 1)
	{
		PCI_HDR->BaseAddress2 = value & 0xfffffffc;
	}
	else
		PCI_HDR->BaseAddress2 = value & 0xfffffff0;
	if(PCI_HDR->BaseAddress2)
	{
		Write_PCI_Config(bus,device,function,24,0xffffffff);
		value = Read_PCI_Config(bus,device,function,24);
		value = value & 0xfffffff0;;
		PCI_HDR->BaseLimit2 = ~value + 1;
		Write_PCI_Config(bus,device,function,24,PCI_HDR->BaseAddress2);
	}
	color_printk(RED,BLACK,"Base Address #2:%#010x,Base Limit #2:%#010x\n",PCI_HDR->BaseAddress2,PCI_HDR->BaseLimit2);

	value = Read_PCI_Config(bus,device,function,28);	//////BAR3
	if(value & 1)
	{
		PCI_HDR->BaseAddress3 = value & 0xfffffffc;
	}
	else
		PCI_HDR->BaseAddress3 = value & 0xfffffff0;
	if(PCI_HDR->BaseAddress3)
	{
		Write_PCI_Config(bus,device,function,28,0xffffffff);
		value = Read_PCI_Config(bus,device,function,28);
		value = value & 0xfffffff0;;
		PCI_HDR->BaseLimit3 = ~value + 1;
		Write_PCI_Config(bus,device,function,28,PCI_HDR->BaseAddress3);
	}
	color_printk(RED,BLACK,"Base Address #3:%#010x,Base Limit #3:%#010x\n",PCI_HDR->BaseAddress3,PCI_HDR->BaseLimit3);

	value = Read_PCI_Config(bus,device,function,32);	//////BAR4
	if(value & 1)
	{
		PCI_HDR->BaseAddress4 = value & 0xfffffffc;
	}
	else
		PCI_HDR->BaseAddress4 = value & 0xfffffff0;
	if(PCI_HDR->BaseAddress4)
	{
		Write_PCI_Config(bus,device,function,32,0xffffffff);
		value = Read_PCI_Config(bus,device,function,32);
		value = value & 0xfffffff0;;
		PCI_HDR->BaseLimit4 = ~value + 1;
		Write_PCI_Config(bus,device,function,32,PCI_HDR->BaseAddress4);
	}
	color_printk(RED,BLACK,"Base Address #4:%#010x,Base Limit #4:%#010x\n",PCI_HDR->BaseAddress4,PCI_HDR->BaseLimit4);

	value = Read_PCI_Config(bus,device,function,36);	//////BAR5
	if(value & 1)
	{
		PCI_HDR->BaseAddress5 = value & 0xfffffffc;
	}
	else
		PCI_HDR->BaseAddress5 = value & 0xfffffff0;
	if(PCI_HDR->BaseAddress5)
	{
		Write_PCI_Config(bus,device,function,36,0xffffffff);
		value = Read_PCI_Config(bus,device,function,36);
		value = value & 0xfffffff0;;
		PCI_HDR->BaseLimit5 = ~value + 1;
		Write_PCI_Config(bus,device,function,36,PCI_HDR->BaseAddress5);
	}
	color_printk(RED,BLACK,"Base Address #5:%#010x,Base Limit #5:%#010x\n",PCI_HDR->BaseAddress5,PCI_HDR->BaseLimit5);

	value = Read_PCI_Config(bus,device,function,40);
	PCI_HDR->CardBusCISPointer = value;
	color_printk(RED,BLACK,"CardBus CIS Pointer:%#010x,",PCI_HDR->CardBusCISPointer);

	value = Read_PCI_Config(bus,device,function,44);
	PCI_HDR->SubSystemVendorID = value & 0xffff;
	PCI_HDR->SubSystemID = (value >> 16) & 0xffff;
	color_printk(RED,BLACK,"SubSystem ID:%#06x,SubSystem Vendor ID:%#06x\n",PCI_HDR->SubSystemID,PCI_HDR->SubSystemVendorID);

	value = Read_PCI_Config(bus,device,function,48);
	PCI_HDR->ExpansionROMBaseAddress = value;
	color_printk(RED,BLACK,"Expansion ROM Base Address:%#010x,",PCI_HDR->ExpansionROMBaseAddress);

	value = Read_PCI_Config(bus,device,function,52);
	PCI_HDR->CapabilitiesPointer = value & 0xff;
	PCI_HDR->Reserved0 = (value >> 8) & 0xffffff;
	color_printk(RED,BLACK,"Capabilities Pointer:%#04x\n",PCI_HDR->CapabilitiesPointer);

	PCI_HDR->Reserved1 = 0;

	value = Read_PCI_Config(bus,device,function,60);
	PCI_HDR->InterruptLine = value & 0xff;
	PCI_HDR->InterruptPIN = (value >> 8) & 0xff;
	PCI_HDR->MinGrant = (value >> 16) & 0xff;
	PCI_HDR->MaxLatency = (value >> 24) & 0xff;
	color_printk(RED,BLACK,"Max Latency:%#04x,Min Grant:%#04x,Interrupt PIN:%#04x,Interrupt Line:%#04x\n",PCI_HDR->MaxLatency,PCI_HDR->MinGrant,PCI_HDR->InterruptPIN,PCI_HDR->InterruptLine);

	index = PCI_HDR->CapabilitiesPointer;

	while(index != 0)
	{
		value = Read_PCI_Config(bus,device,function,index);
		color_printk(GREEN,BLACK,"Capability ID:%#04x,Pointer:%#04x,%#06x,",value & 0xff,((value >> 8) & 0xff),(value >> 16) & 0xffff);
		color_printk(GREEN,BLACK,"%#010x,",Read_PCI_Config(bus,device,function,index + 4));
		color_printk(GREEN,BLACK,"%#010x,",Read_PCI_Config(bus,device,function,index + 8));
		color_printk(GREEN,BLACK,"%#010x\n",Read_PCI_Config(bus,device,function,index + 12));

		index = (value >> 8) & 0xff;
	}

	return 1;
}
